package com.bcx.wind.workflow.imp;

import com.bcx.wind.workflow.WindEngine;
import com.bcx.wind.workflow.access.Access;
import com.bcx.wind.workflow.cache.Cache;
import com.bcx.wind.workflow.cache.WindCache;
import com.bcx.wind.workflow.cache.imp.BlockingCache;
import com.bcx.wind.workflow.cache.imp.DelOverCache;
import com.bcx.wind.workflow.cache.imp.ScheduleCache;
import com.bcx.wind.workflow.core.WindContext;
import com.bcx.wind.workflow.core.constant.WindDB;
import com.bcx.wind.workflow.core.lock.Lock;
import com.bcx.wind.workflow.core.lock.WindLock;
import com.bcx.wind.workflow.entity.WindProcess;
import com.bcx.wind.workflow.entity.WindProcessConfig;
import com.bcx.wind.workflow.exception.WorkflowException;
import com.bcx.wind.workflow.imp.service.*;
import com.bcx.wind.workflow.interceptor.*;
import com.bcx.wind.workflow.jdbc.DefaultWindContext;
import com.bcx.wind.workflow.jdbc.JdbcAccess;
import com.bcx.wind.workflow.message.WorkflowMessage;
import com.bcx.wind.workflow.mybatis.MybatisAccess;
import com.bcx.wind.workflow.pojo.ProcessModule;
import com.bcx.wind.workflow.spring.SpringAccess;
import com.bcx.wind.workflow.support.JsonHelper;
import com.bcx.wind.workflow.support.ObjectHelper;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import static com.bcx.wind.workflow.core.constant.Constant.MODULE_CACHE;

/**
 * 工作流全局配置
 *
 * 集合数据源配置 ， 流程过程定义缓存， 过程定义配置缓存，
 * 持久层接口实现， 持久层接口实现容器，
 *
 * @author zhanglei
 */
public class Configuration {

    private static final Logger LOGGER = LoggerFactory.getLogger(Configuration.class);

    /**
     * 引擎
     */
    private WindEngine windEngine;

    /**
     * 历史服务
     */
    private HistoryService historyService;

    /**
     * 监控管理服务
     */
    private ManagerService managerService;

    /**
     * 静态资源管理服务
     */
    private RepositoryService repositoryService;

    /**
     * 运行时服务
     */
    private RuntimeService runtimeService;


    /**
     * 核心服务
     */
    private WindCoreService windCoreService;

    /**
     * 数据源
     */
    private DataSource dataSource;

    /**
     * 工作流持久层类型
     */
    private WindDB db;

    /**
     * 工作流持久层执行器
     */
    private Access access;

    /**
     * mybatis  sqlSessionFactory 可无
     */
    private SqlSessionFactory  sqlSessionFactory;

    /**
     * 生效的流程资源缓存
     */
    private final Cache  processCache;

    /**
     * 流程定义模块缓存
     */
    private final Cache  processModuleCache;

    /**
     * 流程配置资源缓存
     */
    private final Cache processConfigCache;


    /**
     * 流程待办事宜 流程定义模块缓存数据
     */
    private final Cache toDoProcessModuleCache;

    /**
     * 工作流命令执行器
     */
    private CommandExecutor commandExecutor;


    /**
     * 工作流拦截器集合
     */
    private List<CommandInterceptor>  interceptors;

    /**
     * 工作流消息国际化处理
     */
    private WorkflowMessage message = WorkflowMessage.getInstance();


    /**
     * 工作流锁机制  默认Windlock
     */
    private Lock lock = new WindLock();

    /**
     * 工作流后置拦截器
     */
    private AfterInterceptor afterInterceptor;

    /**
     * 工作流自定义拦截器
     */
    private CustomInterceptor customInterceptor;

    /**
     * 工作流工具容器
     */
    private static WindContext windContext;


    public Configuration(DataSource dataSource,WindDB db){
        this.dataSource = dataSource;
        this.db = db;
        this.processCache = buildProcessCache();
        this.processModuleCache = buildProcessModuleCache();
        this.processConfigCache = buildProcessConfigCache();
        this.toDoProcessModuleCache = buildToDoProcessModuleCache();
    }

    public Configuration(DataSource dataSource,WindDB db,
                         Cache processCache,
                         Cache processModuleCache,
                         Cache processConfigCache,
                         Cache toDoProcessModuleCache){
        this.dataSource = dataSource;
        this.db = db;
        this.processCache = processCache;
        this.processModuleCache = processModuleCache;
        this.processConfigCache = processConfigCache;
        this.toDoProcessModuleCache = toDoProcessModuleCache;
    }


    void initServiceConfig(){
        setServiceCommandExecutor(this.runtimeService);
        setServiceCommandExecutor(this.managerService);
        setServiceCommandExecutor(this.repositoryService);
        setServiceCommandExecutor(this.historyService);
        setServiceCommandExecutor(this.windCoreService);
    }

    private  void  setServiceCommandExecutor(Object service){
        if(service instanceof ServiceImpl){
            ((ServiceImpl) service).configuration(this);
            ((ServiceImpl) service).setExecutor(this.commandExecutor);
        }
    }


    /**
     * 获取当前引擎工具容器
     *
     * @return  工具容器
     */
    public static WindContext  windContext(){
        if(Configuration.windContext == null){
            return new DefaultWindContext();
        }

        return Configuration.windContext();
    }


    /**
     * 设置工具容器
     *
     * @param context  容器
     */
    public static void setWindContext(WindContext context){
        Configuration.windContext = context;
    }


    private  CommandInterceptor  buildFirstInterceptor(){
        if(this.interceptors == null){
            interceptors = new LinkedList<>();
            interceptors.add(new LogInterceptor());
        }else{
            interceptors.add(new LogInterceptor());
        }

        CommandInterceptor interceptor = null;
        for(int i=0 ; i<this.interceptors.size() ; i++){
            interceptor = this.interceptors.get(i);
            if(i < this.interceptors.size()-1){
                interceptor.setNext(this.interceptors.get(i+1));
            }
            if(i==this.interceptors.size()-1) {
                InvokeInterceptor invokeInterceptor = new InvokeInterceptor(
                        new CommandContext(this)
                );
                interceptor.setNext(invokeInterceptor);
                break;
            }
        }
        return interceptor;
    }

    /**
     * 构建拦截执行器
     */
    void  buildCommandExecutor(){
        CommandInterceptor first = buildFirstInterceptor();
        commandExecutor = new CommandExecutorImpl(first);
    }


    /**
     * 删除流程定义缓存
     *
     * @param processId  流程定义ID
     */
    public  void removeProcessCache(String processId){
        synchronized (this.processCache) {
            this.processCache.removeKey(processId);
            this.processConfigCache.removeKey(processId);
        }
    }

    /**
     * 添加流程定义缓存
     *
     * @param process  流程定义
     */
    public synchronized   void  addProcessCache(WindProcess process){
        if(process == null){
            return;
        }
        synchronized (this.processCache) {
            this.processCache.put(process.getId(), process);
        }
    }

    /**
     * 更新processModule  流程定义模块缓存
     *
     * @param processModule  流程定义模块对象
     */
    public synchronized void changeProcessModuleCache(ProcessModule processModule){
        this.processModuleCache.put(MODULE_CACHE,processModule);
    }

    public synchronized ProcessModule  getProcessModule(){
        if(this.processModuleCache == null){
            return null;
        }
        return (ProcessModule) processModuleCache.get(MODULE_CACHE);
    }

    public WindProcess  getProcess(String processId){
        return (WindProcess) this.processCache.get(processId);
    }


    @SuppressWarnings("unchecked")
    public List<WindProcessConfig>  getProcessConfig(String processId){
        try {
            Object processConfigs = this.processConfigCache.get(processId);
            if(processConfigs != null) {
                return JsonHelper.coverObject(processConfigs, List.class, WindProcessConfig.class);
            }
            return Collections.emptyList();
        }catch (Exception e){
            LOGGER.error(e.getMessage(),e);
            //此处不用抛出异常
            return Collections.emptyList();
        }
    }

    /**
     * 配置缓存中添加值
     *
     * @param processId   流程ID
     * @param configList  流程下的配置数据
     */
    public synchronized void  addProcessConfigCache(String processId , List<WindProcessConfig> configList){
        if(processId == null || configList == null){
            return;
        }

        synchronized (this.processConfigCache){
            this.processConfigCache.put(processId,configList);
        }
    }

    /**
     * 更新流程配置缓存数据
     *
     * @param processId   流程定义ID
     * @param config      流程配置
     */
    @SuppressWarnings("unchecked")
    public synchronized void  addProcessConfigCache(String processId,WindProcessConfig config){
        if(processId == null || config == null){
            return;
        }
        try {
            Object processConfigs = this.processConfigCache.get(processId);
            if (!ObjectHelper.isEmpty(processConfigs)) {
                List<WindProcessConfig> configs = JsonHelper.coverObject(processConfigs, List.class, WindProcessConfig.class);
                if (configs.contains(config)) {
                    configs.removeIf(con -> con.getId().equals(config.getId()));
                    configs.add(config);
                } else {
                    configs.add(config);
                }
                this.processConfigCache.put(processId,configs);
            } else {
                List<WindProcessConfig> configList = new LinkedList<>();
                configList.add(config);
                this.processConfigCache.put(processId, configList);
            }
        }catch (Exception e){
            //此处拒绝抛出异常
            LOGGER.error(e.getMessage(),e);
        }

    }

    /**
     * 删除指定的配置缓存数据
     *
     * @param processId   流程定义ID
     * @param configId    配置ID
     */
    @SuppressWarnings("unchecked")
    public synchronized  void  removeProcessConfigCache(String processId,String configId){
        if(processId == null || configId == null){
            return;
        }
        try {
            Object processConfigs = this.processConfigCache.get(processId);
            if (!ObjectHelper.isEmpty(processConfigs)) {
                List<WindProcessConfig> configs = JsonHelper.coverObject(processConfigs, List.class, WindProcessConfig.class);
                configs.removeIf(config -> config.getId().equals(configId));
                this.processConfigCache.put(processId, configs);
            }
        }catch (Exception e){
            LOGGER.error(e.getMessage(),e);
        }
    }


    public synchronized  void removeProcessConfigCache(String processId){
        if(processId == null){
            return;
        }
        this.processConfigCache.removeKey(processId);
    }



    public ProcessModule getToDoProcessModuleCache(String userId,String system){
        String key = system + userId;

        try {
            return (ProcessModule) this.toDoProcessModuleCache.get(key);
        }catch (Exception e){
            LOGGER.error(e.getMessage(),e);
            return null;
        }
    }


    public void addToDoProcessModuleCache(String userId,String system,ProcessModule processModule){
        String key = system + userId;
        if (ObjectHelper.isEmpty(key) || processModule == null) {
            return;
        }
        this.toDoProcessModuleCache.put(key,processModule);
    }


    /**
     * 构建流程定义缓存器
     *
     * @return Cache
     */
    private Cache  buildProcessCache(){
        //超出内存，清除不常用的
        return new DelOverCache(
                //行锁缓存，将操作的缓存数据锁住
                new BlockingCache(
                        //定时清理缓存器 ，默认1小时清理一次
                        new ScheduleCache(
                                //基本缓存器
                                new WindCache()
                        )
                )
        );
    }


    /**
     * 构建流程定义模块缓存器
     *
     * @return 缓存器
     */
    private Cache buildProcessModuleCache() {
        return new BlockingCache(
                new ScheduleCache(
                            new WindCache()
                 )
        );
    }


    private Cache  buildProcessConfigCache(){
        return new DelOverCache(
             new BlockingCache(
                new ScheduleCache(
                        new WindCache()
                )
             )
        );
    }


    /**
     * 待办事宜 流程定义模块 流程定义缓存
     * 定时清除 默认1分钟刷新一次
     *
     * @return  缓存器
     */
    private Cache buildToDoProcessModuleCache() {
        return new BlockingCache(
                new ScheduleCache(
                        new WindCache(),1000*60
                )
        );
    }



    /**
     * 构建工作流持久层执行器
     */
    void buildAccess(){
        if(this.dataSource == null){
            if(LOGGER.isDebugEnabled()){
                LOGGER.debug("the dataSource is null,can not build wind engine!");
            }
        }

        switch (this.db){
            case JDBC:
                this.access =  new JdbcAccess(this.dataSource);
                break;
            case SPRING:
                this.access = new SpringAccess(this.dataSource);
                break;
            case MYBATIS:
                if(this.sqlSessionFactory == null){
                    if(LOGGER.isDebugEnabled()){
                        LOGGER.debug("build wind engine error ,because use mybatis lack of sqlSessionFactory");
                    }
                    throw new WorkflowException("build wind engine error ,because use mybatis lack of sqlSessionFactory");
                }
                this.access = new MybatisAccess(this.sqlSessionFactory);
                break;
                default:
                    this.access =  new JdbcAccess(this.dataSource);
        }
    }

    public WindEngine getWindEngine() {
        return windEngine;
    }

    public void setWindEngine(WindEngine windEngine) {
        this.windEngine = windEngine;
    }

    public HistoryService getHistoryService() {
        return historyService;
    }

    public void setHistoryService(HistoryService historyService) {
        this.historyService = historyService;
    }

    public ManagerService getManagerService() {
        return managerService;
    }

    public void setManagerService(ManagerService managerService) {
        this.managerService = managerService;
    }

    public RepositoryService getRepositoryService() {
        return repositoryService;
    }

    public void setRepositoryService(RepositoryService repositoryService) {
        this.repositoryService = repositoryService;
    }

    public RuntimeService getRuntimeService() {
        return runtimeService;
    }

    public void setRuntimeService(RuntimeService runtimeService) {
        this.runtimeService = runtimeService;
    }

    public SqlSessionFactory getSqlSessionFactory() {
        return sqlSessionFactory;
    }

    public WindCoreService getWindCoreService() {
        return windCoreService;
    }

    public void setWindCoreService(WindCoreService windCoreService) {
        this.windCoreService = windCoreService;
    }

    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    public CustomInterceptor getCustomInterceptor() {
        return customInterceptor;
    }

    public void setCustomInterceptor(CustomInterceptor customInterceptor) {
        this.customInterceptor = customInterceptor;
    }

    public Access getAccess() {
        return access;
    }

    public List<CommandInterceptor> getInterceptors() {
        return interceptors;
    }

    public void setInterceptors(List<CommandInterceptor> interceptors) {
        this.interceptors = interceptors;
    }

    public CommandExecutor getCommandExecutor() {
        return commandExecutor;
    }

    public WorkflowMessage getMessage() {
        return message;
    }

    public AfterInterceptor getAfterInterceptor() {
        return afterInterceptor;
    }

    public void setAfterInterceptor(AfterInterceptor afterInterceptor) {
        this.afterInterceptor = afterInterceptor;
    }

    public Lock getLock() {
        return lock;
    }

    public void setLock(Lock lock) {
        this.lock = lock;
    }
}
